// Copyright (C) 2009 Jesse Jones
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using MmmDoc;
using Mono.Cecil;
using System;
using System.Collections.Generic;

namespace MmmDoc.Database
{
	internal sealed class PropertyMember : Member
	{
		public PropertyMember(Document doc, TypeDef type, PropertyDefinition member) : base(type, member.ToString())
		{
			Key = member.Name;
			Name = member.Name;
			Type = TypeName.Create(doc, member.PropertyType);
			
			if (doc.IsVisible(member.GetMethod) && doc.IsVisible(member.SetMethod))
			{
				if ((member.GetMethod.Attributes & MethodAttributes.MemberAccessMask) >= (member.SetMethod.Attributes & MethodAttributes.MemberAccessMask))
				{
					Access = (MemberAccess) (member.GetMethod.Attributes & MethodAttributes.MemberAccessMask);
					Modifiers = MethodMember.GetModifiers(member.GetMethod);
					GetterModifiers = string.Empty;
					SetterModifiers = string.Empty;
				}
				else
				{
					Access = (MemberAccess) (member.SetMethod.Attributes & MethodAttributes.MemberAccessMask);
					Modifiers = MethodMember.GetModifiers(member.SetMethod);
					GetterModifiers = string.Empty;
					SetterModifiers = string.Empty;
				}
				
				string modifiers = MethodMember.GetModifiers(member.GetMethod);
				if (modifiers != Modifiers)
					GetterModifiers = modifiers;
				
				modifiers = MethodMember.GetModifiers(member.SetMethod);
				if (modifiers != Modifiers)
					SetterModifiers = modifiers;
					
				if (member.HasParameters)
					m_types = DoGetIndexerTypes(doc, member.GetMethod);
			}
			else if (doc.IsVisible(member.GetMethod))
			{
				Access = (MemberAccess) (member.GetMethod.Attributes & MethodAttributes.MemberAccessMask);
				Modifiers = MethodMember.GetModifiers(member.GetMethod);
				GetterModifiers = string.Empty;
					
				if (member.HasParameters)
					m_types = DoGetIndexerTypes(doc, member.GetMethod);
			}
			else if (doc.IsVisible(member.SetMethod))
			{
				Access = (MemberAccess) (member.SetMethod.Attributes & MethodAttributes.MemberAccessMask);
				Modifiers = MethodMember.GetModifiers(member.SetMethod);
				SetterModifiers = string.Empty;
					
				if (member.HasParameters)
					m_types = DoGetIndexerTypes(doc, member.SetMethod);
			}
			
			if (member.HasParameters)
			{
				Name = "this";
				Arguments = new Arguments(doc, member);
			}
			
			var attrs = new List<string>();
			member.CustomAttributes.AddAttributes(attrs);
			CustomAttributes = attrs.ToArray();
		}
		
		public TypeName Type {get; private set;}
		
		/// <summary>Will be null if there is no getter and empty if the getter has the same modifiers as the property.</summary>
		public string GetterModifiers {get; private set;}
		
		/// <summary>Will be null if there is no setter and empty if the setter has the same modifiers as the property.</summary>
		public string SetterModifiers {get; private set;}
		
		/// <summary>May be null (if the property is not an indexer).</summary>
		public Arguments Arguments {get; private set;}
		
		internal override void Disambiguate()
		{
			if (Arguments != null)
			{
				var builder = new System.Text.StringBuilder();
			
				builder.Append(Name);
			
				builder.Append(Arguments.Opener);
				for (int i = 0; i < Arguments.Names.Length; ++i)
				{
					if (Arguments.Modifiers[i] != null)
						builder.Append(Arguments.Modifiers[i]);
					
					builder.Append(Arguments.Types[i].Name);
					builder.Append(" ");
					builder.Append(Arguments.Names[i]);
					
					if (i + 1 < Arguments.Names.Length)
						builder.Append(", ");
				}
				builder.Append(Arguments.Closer);
			
				Key = builder.ToString();
			}
		}
		
		internal override SortKey GetSortKey()
		{
			if (Arguments != null)
				return new SortKey{Name = Name, SubWeight = Arguments.Names.Length};
			else
				return new SortKey{Name = Name};
		}
		
		internal override string GetXmlName(TypeDef type)
		{
			var builder = new System.Text.StringBuilder();
			
			builder.Append("P:");
			
			builder.Append(type.GetXmlName());
			builder.Append('.');
			
			if (m_types == null)
			{
				builder.Append(Name);
			}
			else
			{
				builder.Append("Item(");
				for (int i = 0; i < m_types.Length; ++i)
				{
					builder.Append(m_types[i].GetXmlName());
					
					if (i + 1 < m_types.Length)
						builder.Append(',');
				}
				builder.Append(")");
			}
			
			return builder.ToString();
		}
		
		#region Private Methods
		private TypeName[] DoGetIndexerTypes(Document doc, MethodDefinition method)
		{
			TypeName[] types = new TypeName[method.Parameters.Count];
			
			for (int i = 0; i < method.Parameters.Count; ++i)
			{
				types[i] = TypeName.Create(doc, method.Parameters[i].ParameterType);
			}
			
			return types;
		}
		#endregion
		
		#region Fields
		private TypeName[] m_types;
		#endregion
	}
}
